
#include "imtPublic.h"
#include "imtNetwork.h"
#include "imtClient.h"

namespace iMate {
	
	/** initialise network
	@param server    server address: a numeric host address string 
	  @par dotted-decimal IPv4 address or an IPv6 hex address
    @param port     a numeric string
	@return   success to true
	*/
	bool Network::init(const String& server, const String& port)
	{
		if (!mClient)
		{
			mClient = new Client();
		}
		mClient->setServerInfo(server, port);
		return mClient->init();
	}
	///release everything
	void Network::release()
	{
		if (mClient)
		{
			mClient->release();
			SAFE_DELETE(mClient);
		}
	}

	/**connect to server asigned by setServerInfo(@see setServerInfo)
	@return true if connection success
	*/
	bool Network::connect()
	{
		assert(mClient && "network's init not called");
		return mClient->connect();
	}
	///disconnect from server
	void Network::disconnect()
	{
		assert(mClient && "network's init not called");
		return mClient->disconnect();
	}

	/**handle the cmds in the message queue
	@param maxHandleTime   the max time cosumed in handling messages
	       @par if time out, the function returns
	*/
	void Network::handleCmds(ulong maxHandleTime)
	{
		static const int BufferSize = 1024 * 10;
		static char buffer[BufferSize];

		assert(mClient && "network's init not called");

		mTimer.reset();

		int cmdSize = 0;
		while (mTimer.getMilliseconds() < maxHandleTime)
		{
			cmdSize = mClient->popMessage(buffer, BufferSize);
			if (cmdSize >= sizeof(NullCmd))
			{
				handleCmd((const NullCmd*)buffer, cmdSize);
			}
			else break;
		}
	}

	/**handle one single cmd
	@param cmd  the cmd to be handled
	@param size   the size of the cmd
	@return true if handled successfully
	*/
	bool Network::handleCmd(const NullCmd* cmd, size_t size)
	{
		CmdHandlerIteraotr it = mCmdHandlers.find(cmd->_type);
		if (it != mCmdHandlers.end())
		{
			it->second->listen(this, cmd, size);
			return true;
		}
		return false;
	}

	/**send cmd
    @param cmd    a complete cmd
	@param size   the size of cmd
	@param toSelf   send it to server if false, otherwise push it to receive-message 
	     @par queue to simulating a message's arrival from server
	@return true if send successfully
	*/
	bool  Network::sendCmd(const NullCmd* cmd, size_t size, bool toSelf)
	{
		assert(mClient && "network's init not called");

		return mClient->pushMessage(cmd, size, toSelf) == (int)size;
	}

	/**setup a handler for a special type of cmd
	@param cmdType   the type of cmd to be setup
	@param handler   the cmd handler to be set, 
	    @Note the previous handler will be unload even if "handler" is null
	@return  true if success
	*/
	bool Network::setupCmdHandler(cmdtt cmdType, CmdHandler* handler)
	{
		CmdHandlerIteraotr it = mCmdHandlers.find(cmdType);
		if (it == mCmdHandlers.end())
		{
			if (handler)
			{
				mCmdHandlers.insert(std::make_pair(cmdType, handler));
			}
		}
		else
		{
			delete it->second;
			if (handler)
				it->second = handler;
			else
				mCmdHandlers.erase(it);
		}
		return true;
	}

	///clear and delete handlers
	void Network::clearAllHandlers()
	{
		for (CmdHandlerIteraotr it = mCmdHandlers.begin();
			it != mCmdHandlers.end(); ++it)
		{
			delete it->second;
		}
		mCmdHandlers.clear();
	}

	Network& Network::getSingleton(void)
	{
		assert( ms_Singleton );  return ( *ms_Singleton );
	}
	Network* Network::getSingletonPtr(void)
	{
		return ms_Singleton;
	}

}//iMate

namespace Ogre
{
	template<> iMate::Network* Singleton<iMate::Network>::ms_Singleton = NULL;
}